/*
 * mysocket.c
 *
 *  Created on: 14/apr/2014
 *      Author: kappa
 */


#include "pksocket.h"
#include "regExp.h"
#include "netdb.h"

/********************************************************************************
 * @function 		: closeSocket												*
 * @descr			: Invia un array di caratteri su di un socket,				*
 *					  bufferizzando la scrittura a 64 bytes alla volta.			*
 *					  La gestione degli errori e' demandata alla notifyerror	* 
 * @param connection: il socket su cui inviare le informazioni					*
 * @param response	: Le informazioni da inviare								*
 ********************************************************************************/
void closeBidirSocket(unsigned int *socket){
	shutdown(*socket,2);
	close(*socket);
}


/********************************************************************************
 * @function 		: sendResponse												*
 * @descr			: Invia un array di caratteri su di un socket,				*
 *					  bufferizzando la scrittura a 64 bytes alla volta.			*
 *					  La gestione degli errori e' demandata alla notifyerror	* 
 * @param connection: il socket su cui inviare le informazioni					*
 * @param response	: Le informazioni da inviare								*
 ********************************************************************************/
void sendSockData(unsigned int *connection, char * response){

	int i = 0, contentsize = 0, wCycles = 0, writtenbytes = 0, lastbytes = 0;
	char buffer[64];


	memset(buffer,'\0',64);
	
	if(isNull(response)==1){
		notifyError(ERR_SOCK_WRITE,1,0);
	}

	contentsize = strlen(response);
	wCycles = contentsize/64;
	lastbytes = contentsize%64;
	
	i=0;
	while(wCycles > 0){
		memset(buffer,'\0',64);
		strncpy(buffer, &response[i],64);
			if(send(*connection,buffer,64,writtenbytes)<0){
				notifyError(ERR_SOCK_WRITE,1,0);
			}
			i+=64;
		wCycles--;
	}

	if(lastbytes > 0 ){
		strncpy(buffer, &response[i],lastbytes);
		buffer[lastbytes] = '\0';
			if(send(*connection,buffer,lastbytes,writtenbytes)<0){
				notifyError(ERR_SOCK_WRITE,1,0);
			}
	}
}


/********************************************************************************
 * @function 		: waitReadIncCmd											*
 * @descr			: accetta la connessione di un client e legge dal socket	*
 *					  il comando inviato dal client								*
 * @param srvSock	: socket del server											*
 * @param connection: socket dal quale comunicare col client					*
 * @param serverAddr: struttura di sistema con impostati i parametri del server	*
 * @return			: un puntatore alcomando letto.								*		 
 ********************************************************************************/
struct sockaddr_in * waitIncomingConnections (unsigned int * srvSock,unsigned int * connection,struct sockaddr_in *clientAddr){

	unsigned int addrlen = 0;


	if (listen(*srvSock, 5)==0){
		addrlen = sizeof(clientAddr);
		*connection = (unsigned int) accept(*srvSock,(struct sockaddr *) &clientAddr, &addrlen);
	}
	else{
		notifyError(ERR_SOCK_LISTEN, 1,0);
	}
	return clientAddr;

}

////TODO FNIRE
//void connectToRemoteSmtpServer(int servport , struct sockaddr_in* remoteServer){
//	int fdsock = 0;
//
//	fdsock = socket(AF_INET , SOCK_STREAM , 0);
//	if (fdsock == -1)
//	{
//		printf("Could not create socket");
//	}
//	puts("Socket created");
//
//
//
//	if (connect(fdsock , (struct sockaddr *)&remoteServer , sizeof(remoteServer)) < 0)
//		{
//			perror("connect failed. Error");
//		}
//
//
//}
char * readSockData(unsigned int * connection){

	char *buffer = NULL, *tmp = NULL;
	char *cmd = NULL;
	int readbytes = 0;
	int tmpSize = 0;
	int tmpTotSize = 0;

	do{
		/*Leggo un blocco dati*/
		if(buffer != NULL){
			free(buffer);
			buffer = NULL;
		}

		buffer = readSocketBlock(connection, &readbytes);
		/*Se ho letto qualcosa*/
		if(buffer!=NULL){

			/*Se ho gia' letto altri dati concateno*/
			if(cmd!=NULL){
				tmp = cmd;
				tmpSize = strlen(tmp);
				tmpTotSize += tmpSize;
				cmd = (char *)getMemory(tmpSize+readbytes+1, CHAR_TYPE);
				memset(cmd,'\0',tmpSize+readbytes+1);
				strcat(cmd, tmp);
				strcat(cmd, buffer);
				free(tmp);
			}
			else{
				cmd = (char *)getMemory(readbytes+1, CHAR_TYPE);
				memset(cmd,'\0',readbytes+1);
				strcat(cmd, buffer);
			}

		}
	}while(buffer[readbytes-1]!='\n' && buffer[readbytes-2]!='\r');

	if(buffer != NULL){
		free(buffer);
		buffer = NULL;
	}
	return cmd;
}

/****************************************************************************
 * @function 		: readSocketBlock										*
 * @descr			: Legge un blocco di 63 caratteri da un file e lo		*
 *					  concatena col carattere terminatore di stringa '\0'	*
 *					  caratteri. 											*
 * @param connection: socket dal quale da leggere							*
 * @param bytesread	: area di memoria in cui tornare il numero di bytes		*
 *					  letti.												*
 * @return			: un puntatore alla stringa letta.						*
 *					  Se nulla viene letto ritorna la stringa vuota.		*
 *					  La gestione degli errori e' demandata alla notifyerror* 
 ****************************************************************************/
char *  readSocketBlock(unsigned int *connection, int *readbytes){
	char * buffer = NULL;


	buffer = (char *) getMemory(64,CHAR_TYPE);
	memset(buffer,'\0',64);
	if((*readbytes = recv(*connection, buffer, 63, 0) ) < 0){
		if(buffer != NULL){
			free(buffer);
			buffer = NULL;
		}
		notifyError(ERR_SOCK_READ,0,0);

	}
	return buffer;
}


/****************************************************************************
 * @function 		: initServerSocket											*
 * @descr			: Inizializza un socket									*
 * @param port		: port number del socket								*
 * @param sock		: socket												*
 * @param serverAddr: struttura dati di sistema impostata con i parametri	*
 *					  del server											*
 * @param connectAddr: struttura dati di sistema impostata con i parametri	*
 *					  di connessione col client								* 
 ****************************************************************************/

void initServerSocket(unsigned short int *port, unsigned int *sock, struct sockaddr_in *serverAddr){

	memset(serverAddr, 0, sizeof(*serverAddr));
	serverAddr->sin_family = AF_INET;
	serverAddr->sin_addr.s_addr = htonl(INADDR_ANY);
	serverAddr->sin_port = htons(*port);
	*sock = socket(AF_INET,SOCK_STREAM,0);
	if(*sock == -1){
		notifyError(ERR_SOCK_OPEN,1,0);
	}
	if(bind(*sock,(struct sockaddr *) serverAddr, sizeof(*serverAddr))){
		notifyError(ERR_SOCK_BIND,1,0);
	}
}


//Ritorna un descrittore per lo stream o -1 se e' stato impossibile contattare il server remoto
//unsigned int initClientSocket(char *serverHostName, unsigned short int port){
//
//	unsigned int fdsock = -1;
//	struct sockaddr_in *remoteServer;
//	char ** servAddressList = NULL;
//	int k;
//	regex_t pattern_buffer;
//
//	if(serverHostName == NULL || port < 0 || port > 32000){
//		notifyError(ERR_SOCK_OPEN,1,0);
//	}
//	//Controllo se è un indirizzo ip o un nome. Se ip non chiamo lookuphost.
//	if(REG_NOMATCH!= match_regex(&pattern_buffer,serverHostName, validIpAddressRegex)){
//		servAddressList = lookupHost(serverHostName);
//	}
//	else{
//		servAddressList = (char **) getMemory(2,PCHAR_TYPE);
//		memset(servAddressList, 0,2);
//		servAddressList[0] = serverHostName;
//	}
//
//	if(servAddressList == NULL){
//		notifyError(ERR_SOCK_OPEN,1,0);
//	}
//
//	remoteServer->sin_family = AF_INET;
//	remoteServer->sin_port = htons(port);
//	k = 0;
//	while(servAddressList[k]!=NULL){
//		remoteServer.sin_addr->s_addr = htonl(servAddressList[k]);
//		fdsock = socket(AF_INET , SOCK_STREAM , 0);
//		if(fdsock == -1){
//			notifyError(ERR_SOCK_OPEN,0,0);
//			k++;
//		}
//		else if (connect(fdsock , (struct sockaddr *)&remoteServer , sizeof(remoteServer)) < 0)
//		{
//			notifyError(ERR_SOCK_CONNECT,0,0);
//			k++;
//		}
//		else{
//			break;//Ho ottenuto un descriptor per la connessione. Torno.
//		}
//	}
//
//	k=0;
//	while(servAddressList[k]!=NULL){
//		free(servAddressList[k]);
//		k++;
//	}
//	free(servAddressList);
//
//	return fdsock;
//
//}

int setNonblocking(int fd){
    int flags;

    /* If they have O_NONBLOCK, use the Posix way to do it */
#if defined(O_NONBLOCK)
    /* Fixme: O_NONBLOCK is defined but broken on SunOS 4.1.x and AIX 3.2.5. */
    if (-1 == (flags = fcntl(fd, F_GETFL, 0)))
        flags = 0;
    return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
#else
    /* Otherwise, use the old way of doing it */
    flags = 1;
    return ioctl(fd, FIOBIO, &flags);
#endif
}

//char** lookupHost (char *host){
//  struct addrinfo hints, *res;
//  int errcode;
//  char addrstr[100];
//  void *ptr;
//  char ** addresses = NULL;
//  int k;
//  memset (&hints, 0, sizeof (hints));
//  hints.ai_family = PF_UNSPEC;
//  hints.ai_socktype = SOCK_STREAM;
//  hints.ai_flags |= AI_CANONNAME;
//
//  errcode = getaddrinfo (host, NULL, &hints, &res);
//  if (errcode != 0){
//	  perror ("getaddrinfo");
//	  return NULL;
//  }
//  addresses = (char**)getMemory(64, PCHAR_TYPE);
//  memset(addresses,0,64);
//  k=0;
//  while (res){
//	  inet_ntop (res->ai_family, res->ai_addr->sa_data, addrstr, 100);
//
//	  switch (res->ai_family){
//		case AF_INET:
//		  ptr = &((struct sockaddr_in *) res->ai_addr)->sin_addr;
//		  break;
//		case AF_INET6:
//		  ptr = &((struct sockaddr_in6 *) res->ai_addr)->sin6_addr;
//		  break;
//	  }
//	  inet_ntop (res->ai_family, ptr, addrstr, 100);
//	  addresses[k] = (char *)getMemory(16, CHAR_TYPE);
//	  strcpy(addresses[k], addrstr);
//	  k++;
// }
//  if(k==0){
//	  return NULL;
//  }
//  else{
//	  addresses[k+1]=NULL;
//	  return addresses;
//  }
//}
